package orm

import (
	"time"

	"gitee.com/xiawucha365/sago.v2/core/config"
	"github.com/gomodule/redigo/redis"
)

type DbRedis struct {
	Conn *redis.Pool
}

func NewRedis(cf *config.Config) *DbRedis {

	dbredis := new(DbRedis)
	dbredis.Conn = &redis.Pool{
		//连接池控制
		//Wait:        true,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", cf.Redis.Addr,
				redis.DialConnectTimeout(time.Second*10),
				redis.DialPassword(cf.Redis.Password))
			if err != nil {
				return nil, err
			}

			// 选择数据库
			if cf.Redis.Db != 0 {
				if _, err = c.Do("SELECT", cf.Redis.Db); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, nil

		},
	}
	//连接池数量
	dbredis.Conn.MaxIdle = 100
	dbredis.Conn.MaxActive = 50
	dbredis.Conn.IdleTimeout = 2 * time.Second
	dbredis.Conn.Wait = true
	return dbredis
}

func (m *DbRedis) Close() (err error) {
	return m.Conn.Close()
}

func (m *DbRedis) Del(key string) (err error) {
	con := m.Conn.Get()
	defer con.Close()

	_, err = con.Do("DEL", key)
	return
}

func (m *DbRedis) Unlink(key string) (err error) {
	con := m.Conn.Get()
	defer con.Close()

	_, err = con.Do("UNLINK", key)
	return
}

func (m *DbRedis) Scan(cursor int, pattern string, count int) (int, []string, error) {
	con := m.Conn.Get()
	defer con.Close()
	res, err := con.Do("SCAN", cursor, "MATCH", pattern, "COUNT", count)
	nextCursor, _ := redis.Int(res.([]interface{})[0], nil)
	keys, _ := redis.Strings(res.([]interface{})[1], nil)
	return nextCursor, keys, err
}

func (m *DbRedis) Set(key string, val string) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	if _, err := con.Do("SET", key, val); err != nil {
		return err
	}
	return nil
}

func (m *DbRedis) Get(key string) (reply string, err error) {
	con := m.Conn.Get()
	defer con.Close()
	if reply, err := redis.String(con.Do("GET", key)); err != nil {
		return "", err
	} else {
		return reply, err
	}
}

func (m *DbRedis) Expire(key string, val int) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	if _, err := con.Do("EXPIRE", key, val); err != nil {
		return err
	}
	return nil
}

func (m *DbRedis) HGet(key string, field string) (reply string, err error) {
	con := m.Conn.Get()
	defer con.Close()
	if reply, err := redis.String(con.Do("HGET", key, field)); err != nil {
		return "", err
	} else {
		return reply, err
	}
}

func (m *DbRedis) HGetAll(key string) (reply map[string]string, err error) {
	con := m.Conn.Get()
	defer con.Close()

	if reply, err = redis.StringMap(con.Do("HGETALL", key)); err != nil {
		return make(map[string]string, 0), err
	} else {
		return reply, nil
	}
}

func (m *DbRedis) HSet(key string, field string, val string) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	if _, err := con.Do("HSET", key, field, val); err != nil {
		return err
	}
	return nil
}

func (m *DbRedis) HExists(key string, field string) (reply bool, err error) {
	con := m.Conn.Get()
	defer con.Close()
	if reply, err := redis.Bool(con.Do("HEXISTS", key, field)); err != nil {
		return false, err
	} else {
		return reply, err
	}
}

func (m *DbRedis) HIncr(key string, field string, num int) (reply bool, err error) {
	con := m.Conn.Get()
	defer con.Close()
	if reply, err := redis.Bool(con.Do("HINCRBY", key, field, num)); err != nil {
		return false, err
	} else {
		return reply, err
	}
}

func (m *DbRedis) HDel(key string, field string) (reply int, err error) {
	con := m.Conn.Get()
	defer con.Close()
	if reply, err := redis.Int(con.Do("HDEL", key, field)); err != nil {
		return 0, err
	} else {
		return reply, err
	}
}

func (m *DbRedis) HScan(key string, cursor int, pattern string, count int) (int, map[string]string, error) {
	con := m.Conn.Get()
	defer con.Close()

	args := []interface{}{key, cursor}
	if pattern != "" {
		args = append(args, "MATCH", pattern)
	}
	if count > 0 {
		args = append(args, "COUNT", count)
	}

	res, err := con.Do("HSCAN", args...)
	nextCursor, _ := redis.Int(res.([]interface{})[0], nil)
	entries, _ := redis.Strings(res.([]interface{})[1], nil)

	keyValueMap := make(map[string]string)
	for i := 0; i < len(entries); i += 2 {
		keyValueMap[entries[i]] = entries[i+1]
	}
	return nextCursor, keyValueMap, err
}

func (m *DbRedis) GetBit(key string, offset uint64) (reply int, err error) {
	con := m.Conn.Get()
	defer con.Close()
	if reply, err := redis.Int(con.Do("GETBIT", key, offset)); err != nil {
		return 0, err
	} else {
		return reply, err
	}
}

func (m *DbRedis) SetBit(key string, offset uint64, val string) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	if _, err := con.Do("SETBIT", key, offset, val); err != nil {
		return err
	}
	return nil
}

// list 操作
func (m *DbRedis) LPushBatch(queueName string, keys []string) (err error) {
	if len(keys) == 0 {
		return
	}
	con := m.Conn.Get()
	defer con.Close()
	_, err = con.Do("lpush", redis.Args{}.Add(queueName).AddFlat(keys)...)
	return
}

func (m *DbRedis) LPush(queueName string, key string) (err error) {
	if key == "" {
		return
	}
	con := m.Conn.Get()
	defer con.Close()
	_, err = con.Do("lpush", queueName, key)
	return
}

func (m *DbRedis) LPop(queueName string) (string, error) {
	con := m.Conn.Get()
	defer con.Close()
	data, err := redis.String(con.Do("LPOP", queueName))
	if err != nil && err == redis.ErrNil {
		err = nil
		return "", nil
	}
	return data, err
}

func (m *DbRedis) BRPop(queueName string, timeout int) (data string, err error) {
	con := m.Conn.Get()
	defer con.Close()
	nameAndData, err := redis.Strings(con.Do("brpop", queueName, timeout))
	if err != nil && err == redis.ErrNil {
		err = nil
		return
	}
	if len(nameAndData) > 1 {
		data = nameAndData[1]
	}
	return
}

//sortset 有序集合操作

func (m *DbRedis) ZIncr(setname string, member string) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	_, err = con.Do("zincrby", setname, 1, member)

	return
}

func (m *DbRedis) ZRangeAsc(setname string) (userMap []interface{}, err error) {
	con := m.Conn.Get()
	defer con.Close()
	userMap, err = redis.Values(con.Do("ZRANGE", setname, 0, 10, "WITHSCORES"))
	return
}

func (m *DbRedis) ZRangeDesc(setname string) (userMap []interface{}, err error) {
	con := m.Conn.Get()
	defer con.Close()
	userMap, err = redis.Values(con.Do("ZREVRANGE", setname, 0, 10, "WITHSCORES"))
	return
}

func (m *DbRedis) ZRevrange(setname string, start int, stop int) (userMap map[string]string, err error) {
	con := m.Conn.Get()
	defer con.Close()
	userMap, err = redis.StringMap(con.Do("ZREVRANGE", setname, start, stop, "WITHSCORES"))
	return
}

func (m *DbRedis) ZDel(setname string, key string) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	_, err = con.Do("zrem", setname, key)
	return
}
func (m *DbRedis) ZDelBatch(setname string, keys []string) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	_, err = con.Do("zrem", redis.Args{}.Add(setname).AddFlat(keys)...)
	return
}

// 命令用于移除有序集中，指定分数（score）区间内的所有成员。
func (m *DbRedis) ZremRangeByscore(setname string, min int64, max int64) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	_, err = con.Do("ZREMRANGEBYSCORE", setname, min, max)
	return
}

// 返回有序集合的基数
func (m *DbRedis) ZCARD(setname string) (res int, err error) {
	con := m.Conn.Get()
	defer con.Close()
	res, err = redis.Int(con.Do("ZCARD", setname))
	return
}

// 返回有序集合的基数
func (m *DbRedis) ZAdd(setname string, value int64, key interface{}) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	_, err = con.Do("ZADD", setname, value, key)
	return
}

func (m *DbRedis) SAdd(setname string, keys ...interface{}) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	// 构建命令参数
	args := redis.Args{}.Add(setname).AddFlat(keys)
	_, err = con.Do("SADD", args...)
	return
}

// 返回有序集合的基数
func (m *DbRedis) SCARD(setname string) (res int, err error) {
	con := m.Conn.Get()
	defer con.Close()
	res, err = redis.Int(con.Do("SCARD", setname))
	return
}

func (m *DbRedis) SPOP(setname string, num int) (data []string, err error) {
	con := m.Conn.Get()
	defer con.Close()
	nameAndData, err := redis.Strings(con.Do("SPOP", setname, num))
	if err != nil && err == redis.ErrNil {
		err = nil
		return
	}
	if len(nameAndData) > 0 {
		data = nameAndData
	}
	return
}

func (m *DbRedis) LRange(key string, startIndex, endIndex int) ([]string, error) {
	con := m.Conn.Get()
	defer con.Close()
	return redis.Strings(con.Do("LRANGE", key, startIndex, endIndex))
}

func (m *DbRedis) LRem(key string, count int, value string) (int, error) {
	con := m.Conn.Get()
	defer con.Close()
	return redis.Int(con.Do("LREM", key, count, value))
}

func (m *DbRedis) Incr(setname string) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	_, err = redis.Int64(con.Do("INCR", setname))

	return
}

func (m *DbRedis) IncrBy(setname string, member int) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	_, err = redis.Int64(con.Do("INCRBY", setname, member))

	return
}

func (m *DbRedis) Decr(setname string) (err error) {
	con := m.Conn.Get()
	defer con.Close()
	_, err = redis.Int64(con.Do("DECR", setname))

	return
}
